﻿#include "ISEA4TImpl.hpp"
#include"../Projection/Project.hpp"
#include"ISEA4TQuery.hpp"
#include <iostream>
using namespace NNU::DGGS::Kernel::ISEA4T;
using namespace NNU::DGGS::Kernel::Projection;

void ISEA4TImpl::init()
{
    this->DGGSquery = new ISEA4TQuery();
    this->DGGSquery->init();
    this->isea = new ISEA();
    this->isea->init();
}

void ISEA4TImpl::release()
{
    delete this->DGGSquery;
    delete this->isea;
}

bool ISEA4TImpl::decode(DGGSElementCode code, GeoCoord& coord)
{
    DGGSElementCodeIter* iter = nullptr;
    int elementNum = 0;
    double lon = 0;
    double lat = 0;
    switch (code.elementType)
    {
        case DGGSElementType::Voxel:
        case DGGSElementType::Cell:
        {
            long long int row=code.row;
            long long int col=code.col;
            this->isea->RC2LBCenter( DGGSGridType::DGGS_ISEA4T,code.basePartition, row, col, code.level, lon, lat);
            coord.lon = lon;
            coord.lat = lat;
            break;
        }
        case DGGSElementType::Edge:
        {
            this->query(DGGSTopoType::RelativeVertexes, code, iter, elementNum);
            this->decode(iter->code,  coord);
            delete iter;
            iter = nullptr;
            break;
        }
        case DGGSElementType::Vertex:
        {
            double* vertexes = new double[3];
            this->isea->RC2LBVertex(code.basePartition, code.row, code.col, code.level, DGGSGridType::DGGS_ISEA4T, vertexes);
            coord.lon = vertexes[0];
            coord.lat = vertexes[1];
            delete[]vertexes;
            break;
        }
        default:
            printf("Entered a wrong type of element!\n");
            return false;
    }
    return true;
}


bool ISEA4TImpl::encode(GeoCoord coord, DGGSElementCode& code)
{
    DGGSElementType elementType = code.elementType;
    int elementNum = 0;
    long long int row, col;
    int basePartition = -1;
    this->isea->LB2RC(coord.lon, coord.lat, code.level, DGGSGridType::DGGS_ISEA4T, basePartition, row, col);
    code.basePartition = basePartition;
    code.elementType = DGGSElementType::Cell;
    code.row = row;
    code.col = col;

    int position = 0;
    double difference = 999;
    int p = 0;
    DGGSElementCodeIter* iter= nullptr;
    switch (elementType)
    {
        case DGGSElementType::Vertex:
            this->query(DGGSTopoType::RelativeVertexes, code, iter, elementNum);
            for (int i = 0; i < elementNum; i++)
            {
                GeoCoord vertCoord;
                this->decode(iter[i].code, vertCoord);
                if (fabs(vertCoord.lon - coord.lon) + fabs(vertCoord.lat - coord.lat) < difference)
                {
                    difference = fabs(vertCoord.lon - coord.lon) + fabs(vertCoord.lat - coord.lat);
                    position = i;
                }
            }
            code = iter[position].code;
            delete[]iter;
            iter= nullptr;
            return true;
        case DGGSElementType::Edge:
            this->query(DGGSTopoType::RelativeEdges, code, iter, elementNum);
            for (int i = 0; i < elementNum; i++)
            {
                GeoCoord edgeCoord;
                this->decode(iter[i].code, edgeCoord);
                if (fabs(edgeCoord.lon - coord.lon) + fabs(edgeCoord.lat - coord.lat) < difference)
                {
                    difference = fabs(edgeCoord.lon - coord.lon) + fabs(edgeCoord.lat - coord.lat);
                    position = i;
                }
            }
            code = iter[position].code;
            delete[]iter;
            iter= nullptr;
            return true;
        case DGGSElementType::Cell:
            return true;
        default:
            printf("Entered a wrong type of element!\n");
            return false;
    }
}

bool ISEA4TImpl::query(DGGSTopoType queryType, DGGSElementCode code, DGGSElementCodeIter*& iter, int& elementNum)
{
    elementNum = 1;
    DGGSElementType elementType = code.elementType;
    DGGSElementCode* codes;
    switch (elementType)
    {
        case DGGSElementType::Vertex:
            this->vertexquery(queryType, code, codes, elementNum);
            break;
        case DGGSElementType::Edge:
            this->edgequery(queryType, code, codes, elementNum);
            break;
        case DGGSElementType::Cell:
            this->cellquery(queryType, code, codes, elementNum);
            break;
        case DGGSElementType::Voxel:
            this->voxelquery(queryType, code, codes, elementNum);
            break;
        default:
            printf("Entered a wrong type of element!\n");
            return false;
    }
    iter = new DGGSElementCodeIter [elementNum];
    //赋值给迭代器
    for (int i = 0; i < elementNum; i++)
    {
        iter[i].code = codes[i];
        if (i != 0)
        {
            iter[i - 1].next = &iter[i];
        }
    }
    iter[elementNum-1].next= nullptr;
    delete []codes;
    codes = nullptr;
    return true;
}


bool ISEA4TImpl::cellquery(DGGSTopoType queryType, DGGSElementCode code, DGGSElementCode*& codes, int& elementNum)
{
    elementNum = 3;
    switch (queryType)
    {
        case DGGSTopoType::Parent:
            this->DGGSquery->getCellParent(code, codes,elementNum);
            break;
        case DGGSTopoType::Children:
            this->DGGSquery->getCellChildren(code, codes, elementNum);
            break;
        case DGGSTopoType::Siblings:
            this->DGGSquery->getCellSiblings(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeVertexes:
            this->DGGSquery->fromCellGetRelativeVertexes(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeEdges:
            this->DGGSquery->fromCellGetBoundary(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeCells:
            this->DGGSquery->fromCellGetRelativeCells(code, codes,elementNum);
            break;
        case DGGSTopoType::RelativeVoxels:
            this->DGGSquery->fromCellGetRelativeVoxels(code, codes, elementNum);
            break;
        default:
            printf("Entered a wrong type of query!\n");
            return false;
    }
    return true;
}

bool ISEA4TImpl::edgequery(DGGSTopoType queryType, DGGSElementCode code, DGGSElementCode*& codes, int& elementNum)
{
    switch (queryType)
    {
        case DGGSTopoType::Parent:
            this->DGGSquery->getEdgeParent(code, codes,elementNum);
            break;
        case DGGSTopoType::Children:
            this->DGGSquery->getEdgeChildren(code, codes, elementNum);
            break;
        case DGGSTopoType::Siblings:
            this->DGGSquery->getEdgeSiblings(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeVertexes:
            this->DGGSquery->fromEdgeGetBoundary(code, codes,elementNum);
            break;
        case DGGSTopoType::RelativeEdges:
            this->DGGSquery->fromEdgeGetRelativeEdges(code, codes,elementNum);
            break;
        case DGGSTopoType::RelativeCells:
            this->DGGSquery->fromEdgeGetAssociationBoundary(code, codes,elementNum);
            break;
        case DGGSTopoType::RelativeVoxels:
            this->DGGSquery->fromEdgeGetRelativeVoxels(code, codes, elementNum);
            break;
        default:
            printf("Entered a wrong type of query!\n");
            return false;
    }
    return true;
}

bool ISEA4TImpl::vertexquery(DGGSTopoType queryType, DGGSElementCode code, DGGSElementCode*& codes, int& elementNum)
{
    switch (queryType)
    {
        case DGGSTopoType::Parent:
            this->DGGSquery->getVertexParent(code, codes,elementNum);
            break;
        case DGGSTopoType::Children:
            this->DGGSquery->getVertexChildren(code, codes, elementNum);
            break;
        case DGGSTopoType::Siblings:
            this->DGGSquery->getVertexSiblings(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeVertexes:
            this->DGGSquery->fromVertexGetRelativeVertexes(code, codes,elementNum);
            break;
        case DGGSTopoType::RelativeEdges:
            this->DGGSquery->fromVertexGetAssociationBoundary(code, codes,elementNum);
            break;
        case DGGSTopoType::RelativeCells:
            this->DGGSquery->fromVertexSearchRelativeCells(code, codes ,elementNum);
            break;
        case DGGSTopoType::RelativeVoxels:
            this->DGGSquery->fromVertexGetRelativeVoxels(code, codes, elementNum);
            break;
        default:
            printf("Entered a wrong type of query!\n");
            return false;
    }
    return true;
}

bool ISEA4TImpl::voxelquery(DGGSTopoType queryType, DGGSElementCode code, DGGSElementCode *&codes, int &elementNum) {

    switch (queryType)
    {
        case DGGSTopoType::Parent:
            this->DGGSquery->getVoxelParent(code, codes, elementNum);
            break;
        case DGGSTopoType::Children:
            this->DGGSquery->getVoxelChildren(code, codes, elementNum);
            break;
        case DGGSTopoType::Siblings:
            this->DGGSquery->getVoxelSiblings(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeVertexes:
            this->DGGSquery->fromVoxelGetRelativeVertexes(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeEdges:
            this->DGGSquery->fromVoxelGetRelativeEdges(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeCells:
            this->DGGSquery->fromVoxelGetRelativeCells(code, codes, elementNum);
            break;
        case DGGSTopoType::RelativeVoxels:
            this->DGGSquery->fromVoxelGetRelativeVoxels(code, codes, elementNum);
            break;
        default:
            printf("Entered a wrong type of query!\n");
            return false;
    }
    return true;
}


